const fs = require("fs");
const escope = require("escope");
const esprima = require("esprima");
const iconv = require("iconv-lite");
const escodegen = require("escodegen");

const estraverse = require("estraverse");
// TODO ast解析，替换，还原代码

function ForFix(ast) {
  //for循环代码还原
  ast = estraverse.replace(ast, {
    enter: function (node, parent) {
      if (
        node.type === "ExpressionStatement" && // TODO 代码待优化，for循环下也有可能是其他的语法结构体，不只ExpressionStatement
        parent.type === "ForStatement" &&
        parent.body === node
      ) {
        return {
          type: esprima.Syntax.BlockStatement,
          body: [node],
        };
      }
    },
  });
  return ast;
}


const { decrypt_str, decrypt_str_name } = require("./ob_func"); // 导入出口函数 （OB_Replace_Fix，使用，sojson.v5）

function OB_Replace_Fix(ast) {
    
  ast = estraverse.replace(ast, {   // 将出口函数的调用部分替换成他的运行结果
      enter: function (node, parent) {
          if (
          node.type === "Property" && 
          node.value.type === "CallExpression" &&
          node.value.callee.name === decrypt_str_name &&
          node.value.arguments.length === 1
          ) {
              a = decrypt_str(node.value.arguments[0].value)
              console.log("函数 > ",decrypt_str_name,"运行结果 > ",a)
              return {
                  type: node.type,
                  key:node.key,
                  computed:node.computed,
                  value:{
                      type:esprima.Syntax.Literal,
                      value: decrypt_str(node.value.arguments[0].value)
                  }
              };
          
          }else{
              // console.log("没有匹配到")
          }
      },
  });
  return ast;
}

function OB_list_call_Replace_Fix(ast) {
  ast = estraverse.replace(ast, {   // 将出口函数的调用部分替换成他的运行结果,[]式的数组元素调用方式
      enter: function (node, parent) {
          if (
          node.type === "CallExpression" && 
          node.callee.name === decrypt_str_name &&
          node.arguments.length === 1
          ) {
              a = decrypt_str(node.arguments[0].value)
              console.log("函数 > ",decrypt_str_name,"运行结果 > ",a)
              return {
                  type:esprima.Syntax.Literal,
                  value: decrypt_str(node.arguments[0].value) 
              };
          
          }else{
              // console.log("没有匹配到")
          }
      },
  });
  return ast;
}


function Args_Replace_Fix(ast) {    // 自执行函数，形参，实参替换，统一参数名称
  ast = estraverse.replace(ast, {
    enter: function (node, parent) {
      if (
          node.type === "CallExpression" && 
          node.callee.type === "FunctionExpression" &&
          node.arguments.length > 0 &&
          node.callee.params.length > 0
      ) {
          // 获取形参和实参
          var shi_args = node.arguments;
          var xing_args = node.callee.params;

          for (var i = 0; i < shi_args.length; i++) {  // 因为实参可能比形参少，所以遍历实参
              var shi_args_name = shi_args[i].name
              var xing_args_name = xing_args[i].name
              ast = Args_range_node_Fix(node,shi_args_name,xing_args_name)
              // console.log(shi_args_name);
              // console.log(xing_args_name);
              xing_args[i].name = shi_args[i].name// 最后，将形参定义位置的形参，替换成实参，此方法有风险，当实参个数小于形参个数的时候，会导致代码报错   
          }  
      }
    },
  });
  return ast;
}
function Args_range_node_Fix(ast,shi_args_name,xing_args_name) {    // 获取形参和实参后，遍历结点，用实参替换形参
  ast = estraverse.replace(ast, {
    enter: function (node, parent) {
      if (
          node.type === "MemberExpression" &&
          node.object.name === xing_args_name 
      ) {
          node.object.name = shi_args_name
          console.log("形参：",xing_args_name,"替换成 > 实参：", shi_args_name)
      }
    },
  });
  return ast;
}




function VarFix(ast) {
  //var变量声明，表达式拆分
  ast = estraverse.replace(ast, {
    enter: function (node, parent) {
      if (node.type === "VariableDeclaration" && node.declarations.length > 1) {
        // node.declarations.length > 1 证明是var一行表达式
        switch (parent.type) {
          case "Program":
          case "BlockStatement": // 满足父节点是Program 或  BlockStatement
            for (var idx = 0; idx < node.declarations.length; idx++) {
              // 遍历子节点的declaration，将每一个元素变成自己的兄弟节点
              parent.body.splice(parent.body.indexOf(node), 0, {
                // splice()函数，向数组插入或删除元素，第一个参数是插入的位置，第二个参数，0表示插入，1表示删除，第三个参数，要插入的元素
                type: esprima.Syntax.VariableDeclaration,
                kind: node.kind,
                declarations: [
                  node.declarations[idx], // 取出改节点的declarations中的第idx个元素，赋值
                ],
              });
            }
            parent.body.splice(parent.body.indexOf(idx), 1);
            break;
        }
      }
    },
  });
  return ast;
}

function SwitchCaseFix(ast) {
  //SwitchCase还原
  ast = estraverse.replace(ast, {
    enter: function (node) {
      // Replace it with replaced.
      if (
        node.type == "SwitchCase" &&
        node.test &&
        node.test.type == "BinaryExpression"
      ) {
        var val = eval(escodegen.generate(node.test));
        return {
          type: esprima.Syntax.SwitchCase,
          test: {
            type: esprima.Syntax.Literal,
            value: val,
            raw: val,
          },
          consequent: node.consequent,
        };
      }
    },
  });
  return ast;
}

function WhileTrueFix(ast) {
  // 反控制流平坦化
  ast = estraverse.replace(ast, {
    enter: function (node) {
      // 获取作用域
      if (/Function/.test(node.type)) {
        currentScope = scopeManager.acquire(node); // get current function scope
      }
      var keys = [];
      for (var key of currentScope.set.keys()) {
        keys.push(key);
      }
      var casename = null;
      if (
        node.type == "WhileStatement" &&
        node.test &&
        node.test.type == "Identifier"
      ) {
        casename = node.test.name;
        if (
          node.body &&
          node.body.type == "BlockStatement" &&
          casename != null
        ) {
          if (node.body.body && node.body.body.length > 0) {
            for (var idx = 0; idx < node.body.body.length; idx++) {
              if (node.body.body[idx].type == "SwitchStatement") {
                if (
                  node.body.body[idx].discriminant &&
                  node.body.body[idx].discriminant.type == "Identifier" &&
                  node.body.body[idx].discriminant.name &&
                  node.body.body[idx].discriminant.name == casename
                ) {
                  // while(a){
                  //     switch(a){
                  //         case ...
                  //     }
                  // }
                  // case_start_value = eval()
                  var values = {};
                  var igorne_name = ["arguments"];
                  for (var value of currentScope.variables) {
                    if (igorne_name.indexOf(value.name) < 0) {
                      if (
                        value.defs &&
                        value.defs.length == 1 &&
                        value.defs[0].type == "Variable"
                      ) {
                        if (
                          value.defs[0].name &&
                          value.defs[0].name.type &&
                          value.defs[0].name.type == "Identifier"
                        ) {
                          if (
                            value.defs[0].node &&
                            value.defs[0].node.type &&
                            value.defs[0].node.type == "VariableDeclarator"
                          ) {
                            if (
                              value.defs[0].node.init &&
                              value.defs[0].node.init.type &&
                              value.defs[0].node.init.type == "Literal"
                            ) {
                              values[value.name] =
                                value.defs[0].node.init.value;
                            }
                          }
                        }
                      }
                    }
                  }
                  if (keys.indexOf(casename)) {
                    var case_start_value = values[casename];
                    var case_list = {};
                    var flag = true;
                    for (var i = 0; i < node.body.body[idx].cases.length; i++) {
                      if (
                        node.body.body[idx].cases[i].test &&
                        node.body.body[idx].cases[i].test.type == "Literal"
                      ) {
                        case_list[node.body.body[idx].cases[i].test.value] =
                          node.body.body[idx].cases[i].consequent[0];
                      } else {
                        flag = false;
                      }
                    }
                    if (flag) {
                      var start_case = case_list[case_start_value];
                      var end_case = null;
                      var end_case_value = null;
                      var isreturn = false;
                      for (var ii in case_list) {
                        var case_example = case_list[ii];
                        if (
                          case_example.body &&
                          case_example.body.length > 0 &&
                          case_example.body.pop().type == "BreakStatement"
                        ) {
                          if (
                            case_example.body[case_example.body.length - 1]
                              .type == "ExpressionStatement"
                          ) {
                            if (
                              case_example.body[case_example.body.length - 1]
                                .expression &&
                              case_example.body[case_example.body.length - 1]
                                .expression.type == "AssignmentExpression" &&
                              case_example.body[case_example.body.length - 1]
                                .expression.right.type != "Literal"
                            ) {
                              end_case = case_example.body;
                              end_case_value = parseInt(ii);
                            }
                          } else {
                            flag = false;
                          }
                        } else {
                          flag = false;
                        }
                      }
                      if (end_case_value == null) {
                        for (var ii in case_list) {
                          var case_example = case_list[ii];
                          if (
                            case_example.body &&
                            case_example.body.length > 0 &&
                            case_example.body[case_example.body.length - 1]
                              .type == "ExpressionStatement"
                          ) {
                            if (
                              case_example.body[case_example.body.length - 2]
                                .type == "ReturnStatement"
                            ) {
                              end_case = case_example.body;
                              end_case_value = parseInt(ii);
                              isreturn = true;
                              flag = true;
                            } else {
                              flag = false;
                            }
                          } else {
                            flag = false;
                          }
                        }
                      }
                      if (flag) {
                        var struct_body = {
                          type: esprima.Syntax.WhileStatement,
                          test: {
                            type: esprima.Syntax.Identifier,
                            name: casename,
                          },
                          body: {
                            type: esprima.Syntax.BlockStatement,
                            body: [],
                          },
                        };
                        var body = [];
                        var body_end = {
                          type: esprima.Syntax.IfStatement,
                          test: {
                            type: esprima.Syntax.Identifier,
                            name: casename,
                          },
                          consequent: {
                            type: esprima.Syntax.BlockStatement,
                          },
                          alternate: {
                            type: esprima.Syntax.BlockStatement,
                            body: [
                              {
                                type: esprima.Syntax.BreakStatement,
                                label: null,
                              },
                            ],
                          },
                        };
                        var case_id = case_start_value;
                        while (flag) {
                          var case_now = case_list[case_id];
                          if (case_id == case_start_value) {
                            var b = {
                              type: esprima.Syntax.IfStatement,
                              test: {
                                type: esprima.Syntax.BinaryExpression,
                                operator: "==",
                                left: {
                                  type: esprima.Syntax.Identifier,
                                  name: casename,
                                },
                                right: {
                                  type: esprima.Syntax.Literal,
                                  value: case_start_value,
                                  raw: case_start_value,
                                },
                              },
                              consequent: {
                                type: esprima.Syntax.BlockStatement,
                                body: [],
                              },
                            };
                            for (
                              var iii = 0;
                              iii < case_now.body.length;
                              iii++
                            ) {
                              b.consequent.body.push(case_now.body[iii]);
                            }
                            body.push(b);
                          } else if (
                            case_id == end_case_value &&
                            isreturn == false
                          ) {
                            if (
                              body_end.consequent.body &&
                              body_end.consequent.body.length >= 0
                            ) {
                              body.push(body_end);
                              break;
                            } else {
                              for (
                                var iii = 0;
                                iii < case_now.body.length;
                                iii++
                              ) {
                                body.push(case_now.body[iii]);
                              }
                              case_id =
                                case_now.body[case_now.body.length - 1]
                                  .expression.right.consequent.value;
                              body_end.consequent.body = [];
                              continue;
                            }
                          } else {
                            if (
                              body_end.consequent.body &&
                              body_end.consequent.body.length >= 0
                            ) {
                              for (
                                var iii = 0;
                                iii < case_now.body.length;
                                iii++
                              ) {
                                body_end.consequent.body.push(
                                  case_now.body[iii]
                                );
                              }
                            } else {
                              for (
                                var iii = 0;
                                iii < case_now.body.length;
                                iii++
                              ) {
                                body.push(case_now.body[iii]);
                              }
                            }
                          }

                          if (isreturn) {
                            debugger;
                          }
                          if (case_id == end_case_value && isreturn) {
                            debugger;
                            break;
                          }
                          case_id =
                            case_now.body[case_now.body.length - 1].expression
                              .right.value;
                        }
                        struct_body.body.body = body;
                        return struct_body;
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    leave: function (node) {
      if (/Function/.test(node.type)) {
        currentScope = currentScope.upper; // set to parent scope
      }
    },
  });
  return ast;
}

module.exports = {
  // 将写好的工具类导出
  ForFix,
  VarFix,
  SwitchCaseFix,
  WhileTrueFix,
};
